﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
using System.IO.Ports;
using System.Net;
using System.Net.Sockets;
using System.Runtime.CompilerServices;
using System.Threading;
using IPC.Communication.Framework.BasicFramework;
using IPC.Communication.Framework.Core;
using IPC.Communication.Framework.Core.Net;
using IPC.Communication.Framework.Reflection;

namespace IPC.Communication.Framework.Profinet.Fuji
{
    public class FujiSPBServer : NetworkDataServerBase
    {
        private SerialPort serialPort;

        private SoftBuffer xBuffer;

        private SoftBuffer yBuffer;

        private SoftBuffer mBuffer;

        private SoftBuffer dBuffer;

        private SoftBuffer rBuffer;

        private SoftBuffer wBuffer;

        private const int DataPoolLength = 65536;

        private int station = 1;

        public DataFormat DataFormat
        {
            get
            {
                return base.ByteTransform.DataFormat;
            }
            set
            {
                base.ByteTransform.DataFormat = value;
            }
        }

        public bool IsStringReverse
        {
            get
            {
                return base.ByteTransform.IsStringReverseByteWord;
            }
            set
            {
                base.ByteTransform.IsStringReverseByteWord = value;
            }
        }

        public int Station
        {
            get
            {
                return station;
            }
            set
            {
                station = value;
            }
        }

        public FujiSPBServer()
        {
            xBuffer = new SoftBuffer(65536);
            yBuffer = new SoftBuffer(65536);
            mBuffer = new SoftBuffer(65536);
            dBuffer = new SoftBuffer(131072);
            rBuffer = new SoftBuffer(131072);
            wBuffer = new SoftBuffer(131072);
            base.ByteTransform = new RegularByteTransform();
            base.ByteTransform.DataFormat = DataFormat.CDAB;
            base.WordLength = 1;
            serialPort = new SerialPort();
        }

        protected override byte[] SaveToBytes()
        {
            byte[] array = new byte[589824];
            xBuffer.GetBytes().CopyTo(array, 0);
            yBuffer.GetBytes().CopyTo(array, 65536);
            mBuffer.GetBytes().CopyTo(array, 131072);
            dBuffer.GetBytes().CopyTo(array, 196608);
            rBuffer.GetBytes().CopyTo(array, 327680);
            wBuffer.GetBytes().CopyTo(array, 458752);
            return array;
        }

        protected override void LoadFromBytes(byte[] content)
        {
            if (content.Length < 589824)
            {
                throw new Exception("File is not correct");
            }
            xBuffer.SetBytes(content, 0, 65536);
            yBuffer.SetBytes(content, 65536, 65536);
            mBuffer.SetBytes(content, 131072, 65536);
            dBuffer.SetBytes(content, 196608, 131072);
            rBuffer.SetBytes(content, 327680, 131072);
            wBuffer.SetBytes(content, 458752, 131072);
        }

        [HslMqttApi("ReadByteArray", "")]
        public override OperateResult<byte[]> Read(string address, ushort length)
        {
            OperateResult<byte[]> operateResult = new OperateResult<byte[]>();
            try
            {
                switch (address[0])
                {
                    case 'X':
                    case 'x':
                        return OperateResult.CreateSuccessResult(xBuffer.GetBytes(Convert.ToInt32(address.Substring(1)) * 2, length * 2));
                    case 'Y':
                    case 'y':
                        return OperateResult.CreateSuccessResult(yBuffer.GetBytes(Convert.ToInt32(address.Substring(1)) * 2, length * 2));
                    case 'M':
                    case 'm':
                        return OperateResult.CreateSuccessResult(mBuffer.GetBytes(Convert.ToInt32(address.Substring(1)) * 2, length * 2));
                    case 'D':
                    case 'd':
                        return OperateResult.CreateSuccessResult(dBuffer.GetBytes(Convert.ToInt32(address.Substring(1)) * 2, length * 2));
                    case 'R':
                    case 'r':
                        return OperateResult.CreateSuccessResult(rBuffer.GetBytes(Convert.ToInt32(address.Substring(1)) * 2, length * 2));
                    case 'W':
                    case 'w':
                        return OperateResult.CreateSuccessResult(wBuffer.GetBytes(Convert.ToInt32(address.Substring(1)) * 2, length * 2));
                    default:
                        throw new Exception(StringResources.Language.NotSupportedDataType);
                }
            }
            catch (Exception ex)
            {
                operateResult.Message = ex.Message;
                return operateResult;
            }
        }

        [HslMqttApi("WriteByteArray", "")]
        public override OperateResult Write(string address, byte[] value)
        {
            OperateResult<byte[]> operateResult = new OperateResult<byte[]>();
            try
            {
                switch (address[0])
                {
                    case 'X':
                    case 'x':
                        xBuffer.SetBytes(value, Convert.ToInt32(address.Substring(1)) * 2);
                        return OperateResult.CreateSuccessResult();
                    case 'Y':
                    case 'y':
                        yBuffer.SetBytes(value, Convert.ToInt32(address.Substring(1)) * 2);
                        return OperateResult.CreateSuccessResult();
                    case 'M':
                    case 'm':
                        mBuffer.SetBytes(value, Convert.ToInt32(address.Substring(1)) * 2);
                        return OperateResult.CreateSuccessResult();
                    case 'D':
                    case 'd':
                        dBuffer.SetBytes(value, Convert.ToInt32(address.Substring(1)) * 2);
                        return OperateResult.CreateSuccessResult();
                    case 'R':
                    case 'r':
                        rBuffer.SetBytes(value, Convert.ToInt32(address.Substring(1)) * 2);
                        return OperateResult.CreateSuccessResult();
                    case 'W':
                    case 'w':
                        wBuffer.SetBytes(value, Convert.ToInt32(address.Substring(1)) * 2);
                        return OperateResult.CreateSuccessResult();
                    default:
                        throw new Exception(StringResources.Language.NotSupportedDataType);
                }
            }
            catch (Exception ex)
            {
                operateResult.Message = ex.Message;
                return operateResult;
            }
        }

        [HslMqttApi("ReadBoolArray", "")]
        public override OperateResult<bool[]> ReadBool(string address, ushort length)
        {
            try
            {
                int destIndex = 0;
                if (address.LastIndexOf('.') > 0)
                {
                    destIndex = HslHelper.GetBitIndexInformation(ref address);
                    destIndex = Convert.ToInt32(address.Substring(1)) * 16 + destIndex;
                }
                else if (address[0] == 'X' || address[0] == 'x' || address[0] == 'Y' || address[0] == 'y' || address[0] == 'M' || address[0] == 'm')
                {
                    destIndex = Convert.ToInt32(address.Substring(1));
                }
                switch (address[0])
                {
                    case 'X':
                    case 'x':
                        return OperateResult.CreateSuccessResult(xBuffer.GetBool(destIndex, length));
                    case 'Y':
                    case 'y':
                        return OperateResult.CreateSuccessResult(yBuffer.GetBool(destIndex, length));
                    case 'M':
                    case 'm':
                        return OperateResult.CreateSuccessResult(mBuffer.GetBool(destIndex, length));
                    case 'D':
                    case 'd':
                        return OperateResult.CreateSuccessResult(dBuffer.GetBool(destIndex, length));
                    case 'R':
                    case 'r':
                        return OperateResult.CreateSuccessResult(rBuffer.GetBool(destIndex, length));
                    case 'W':
                    case 'w':
                        return OperateResult.CreateSuccessResult(wBuffer.GetBool(destIndex, length));
                    default:
                        throw new Exception(StringResources.Language.NotSupportedDataType);
                }
            }
            catch (Exception ex)
            {
                return new OperateResult<bool[]>(ex.Message);
            }
        }

        [HslMqttApi("WriteBoolArray", "")]
        public override OperateResult Write(string address, bool[] value)
        {
            try
            {
                int num = 0;
                if (address.LastIndexOf('.') > 0)
                {
                    HslHelper.GetBitIndexInformation(ref address);
                    num = Convert.ToInt32(address.Substring(1)) * 16 + num;
                }
                else if (address[0] == 'X' || address[0] == 'x' || address[0] == 'Y' || address[0] == 'y' || address[0] == 'M' || address[0] == 'm')
                {
                    num = Convert.ToInt32(address.Substring(1));
                }
                switch (address[0])
                {
                    case 'X':
                    case 'x':
                        xBuffer.SetBool(value, num);
                        return OperateResult.CreateSuccessResult();
                    case 'Y':
                    case 'y':
                        yBuffer.SetBool(value, num);
                        return OperateResult.CreateSuccessResult();
                    case 'M':
                    case 'm':
                        mBuffer.SetBool(value, num);
                        return OperateResult.CreateSuccessResult();
                    case 'D':
                    case 'd':
                        dBuffer.SetBool(value, num);
                        return OperateResult.CreateSuccessResult();
                    case 'R':
                    case 'r':
                        rBuffer.SetBool(value, num);
                        return OperateResult.CreateSuccessResult();
                    case 'W':
                    case 'w':
                        wBuffer.SetBool(value, num);
                        return OperateResult.CreateSuccessResult();
                    default:
                        throw new Exception(StringResources.Language.NotSupportedDataType);
                }
            }
            catch (Exception ex)
            {
                return new OperateResult<bool[]>(ex.Message);
            }
        }

        //protected override void ThreadPoolLoginAfterClientCheck(Socket socket, IPEndPoint endPoint)
        //{
        //    AppSession appSession = new AppSession();
        //    appSession.IpEndPoint = endPoint;
        //    appSession.WorkSocket = socket;
        //    if (socket.BeginReceiveResult(SocketAsyncCallBack, appSession).IsSuccess)
        //    {
        //        AddClient(appSession);
        //    }
        //    else
        //    {
        //        base.LogNet?.WriteDebug(ToString(), string.Format(StringResources.Language.ClientOfflineInfo, endPoint));
        //    }
        //}

        //[AsyncStateMachine(typeof(<SocketAsyncCallBack>d__17))]
        //[DebuggerStepThrough]
        //private void SocketAsyncCallBack(IAsyncResult ar)
        //{
        //    <SocketAsyncCallBack>d__17 stateMachine = new <SocketAsyncCallBack>d__17();
        //    stateMachine.<>t__builder = AsyncVoidMethodBuilder.Create();
        //    stateMachine.<>4__this = this;
        //    stateMachine.ar = ar;
        //    stateMachine.<>1__state = -1;
        //    stateMachine.<>t__builder.Start(ref stateMachine);
        //}

        private byte[] CreateResponseBack(byte err, string command, byte[] data, bool addLength = true)
        {
            StringBuilder stringBuilder = new StringBuilder();
            stringBuilder.Append(':');
            stringBuilder.Append(Station.ToString("X2"));
            stringBuilder.Append("00");
            stringBuilder.Append(command.Substring(9, 4));
            stringBuilder.Append(err.ToString("X2"));
            if (err == 0 && data != null)
            {
                if (addLength)
                {
                    stringBuilder.Append(FujiSPBHelper.AnalysisIntegerAddress(data.Length / 2));
                }
                stringBuilder.Append(data.ToHexString());
            }
            stringBuilder[3] = ((stringBuilder.Length - 5) / 2).ToString("X2")[0];
            stringBuilder[4] = ((stringBuilder.Length - 5) / 2).ToString("X2")[1];
            stringBuilder.Append("\r\n");
            return Encoding.ASCII.GetBytes(stringBuilder.ToString());
        }

        private int AnalysisAddress(string address)
        {
            string value = address.Substring(2) + address.Substring(0, 2);
            return Convert.ToInt32(value);
        }

        private byte[] ReadFromSPBCore(byte[] receive)
        {
            if (receive.Length < 15)
            {
                return null;
            }
            if (receive[receive.Length - 2] == 13 && receive[receive.Length - 1] == 10)
            {
                receive = receive.RemoveLast(2);
            }
            string @string = Encoding.ASCII.GetString(receive);
            int num = Convert.ToInt32(@string.Substring(3, 2), 16);
            if (num != (@string.Length - 5) / 2)
            {
                return CreateResponseBack(3, @string, null);
            }
            if (@string.Substring(9, 4) == "0000")
            {
                return ReadByCommand(@string);
            }
            if (@string.Substring(9, 4) == "0100")
            {
                return WriteByCommand(@string);
            }
            if (@string.Substring(9, 4) == "0102")
            {
                return WriteBitByCommand(@string);
            }
            return null;
        }

        private byte[] ReadByCommand(string command)
        {
            string a = command.Substring(13, 2);
            int num = AnalysisAddress(command.Substring(15, 4));
            int num2 = AnalysisAddress(command.Substring(19, 4));
            if (num2 > 105)
            {
                CreateResponseBack(3, command, null);
            }
            if (a == "0C")
            {
                return CreateResponseBack(0, command, dBuffer.GetBytes(num * 2, num2 * 2));
            }
            if (a == "0D")
            {
                return CreateResponseBack(0, command, rBuffer.GetBytes(num * 2, num2 * 2));
            }
            if (a == "0E")
            {
                return CreateResponseBack(0, command, wBuffer.GetBytes(num * 2, num2 * 2));
            }
            if (a == "01")
            {
                return CreateResponseBack(0, command, xBuffer.GetBytes(num * 2, num2 * 2));
            }
            if (a == "00")
            {
                return CreateResponseBack(0, command, yBuffer.GetBytes(num * 2, num2 * 2));
            }
            if (a == "02")
            {
                return CreateResponseBack(0, command, mBuffer.GetBytes(num * 2, num2 * 2));
            }
            return CreateResponseBack(2, command, null);
        }

        private byte[] WriteByCommand(string command)
        {
            if (!base.EnableWrite)
            {
                return CreateResponseBack(2, command, null);
            }
            string a = command.Substring(13, 2);
            int num = AnalysisAddress(command.Substring(15, 4));
            int num2 = AnalysisAddress(command.Substring(19, 4));
            if (num2 * 4 != command.Length - 23)
            {
                return CreateResponseBack(3, command, null);
            }
            byte[] data = command.Substring(23).ToHexBytes();
            if (a == "0C")
            {
                dBuffer.SetBytes(data, num * 2);
                return CreateResponseBack(0, command, null);
            }
            if (a == "0D")
            {
                rBuffer.SetBytes(data, num * 2);
                return CreateResponseBack(0, command, null);
            }
            if (a == "0E")
            {
                wBuffer.SetBytes(data, num * 2);
                return CreateResponseBack(0, command, null);
            }
            if (a == "00")
            {
                yBuffer.SetBytes(data, num * 2);
                return CreateResponseBack(0, command, null);
            }
            if (a == "02")
            {
                mBuffer.SetBytes(data, num * 2);
                return CreateResponseBack(0, command, null);
            }
            return CreateResponseBack(2, command, null);
        }

        private byte[] WriteBitByCommand(string command)
        {
            if (!base.EnableWrite)
            {
                return CreateResponseBack(2, command, null);
            }
            string a = command.Substring(13, 2);
            int num = AnalysisAddress(command.Substring(15, 4));
            int num2 = Convert.ToInt32(command.Substring(19, 2));
            bool value = command.Substring(21, 2) != "00";
            if (a == "0C")
            {
                dBuffer.SetBool(value, num * 8 + num2);
                return CreateResponseBack(0, command, null);
            }
            if (a == "0D")
            {
                rBuffer.SetBool(value, num * 8 + num2);
                return CreateResponseBack(0, command, null);
            }
            if (a == "0E")
            {
                wBuffer.SetBool(value, num * 8 + num2);
                return CreateResponseBack(0, command, null);
            }
            if (a == "00")
            {
                yBuffer.SetBool(value, num * 8 + num2);
                return CreateResponseBack(0, command, null);
            }
            if (a == "02")
            {
                mBuffer.SetBool(value, num * 8 + num2);
                return CreateResponseBack(0, command, null);
            }
            return CreateResponseBack(2, command, null);
        }

        public void StartSerial(string com)
        {
            StartSerial(com, 9600);
        }

        public void StartSerial(string com, int baudRate)
        {
            StartSerial(delegate(SerialPort sp)
            {
                sp.PortName = com;
                sp.BaudRate = baudRate;
                sp.DataBits = 8;
                sp.Parity = Parity.None;
                sp.StopBits = StopBits.One;
            });
        }

        public void StartSerial(Action<SerialPort> inni)
        {
            if (!serialPort.IsOpen)
            {
                if (inni != null)
                {
                    inni.Invoke(serialPort);
                }
                serialPort.ReadBufferSize = 1024;
                serialPort.ReceivedBytesThreshold = 1;
                serialPort.Open();
                serialPort.DataReceived += SerialPort_DataReceived;
            }
        }

        public void CloseSerial()
        {
            if (serialPort.IsOpen)
            {
                serialPort.Close();
            }
        }

        private void SerialPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
        {
            int num = 0;
            byte[] array = new byte[1024];
            int num2;
            do
            {
                Thread.Sleep(20);
                num2 = serialPort.Read(array, num, serialPort.BytesToRead);
                num += num2;
            }
            while (num2 != 0);
            if (num == 0)
            {
                return;
            }
            byte[] array2 = array.SelectBegin(num);
            if (array2.Length < 5)
            {
                if (base.LogNet != null)
                {
                    base.LogNet.WriteError(ToString(), "[" + serialPort.PortName + "] Uknown Data：" + array2.ToHexString(' '));
                }
            }
            else
            {
                if (array2[0] != 58)
                {
                    return;
                }
                if (base.LogNet != null)
                {
                    base.LogNet.WriteDebug(ToString(), "[" + serialPort.PortName + "] Ascii " + StringResources.Language.Receive + "：" + Encoding.ASCII.GetString(array2.RemoveLast(2)));
                }
                if (Encoding.ASCII.GetString(array2, 1, 2) != station.ToString("X2"))
                {
                    if (base.LogNet != null)
                    {
                        base.LogNet.WriteDebug(ToString(), String.Format("[{0}] Station not match , Except: {1:X2} , Actual: {2}", serialPort.PortName, station, Encoding.ASCII.GetString(array2, 1, 2)));
                    }
                    return;
                }
                byte[] array3 = ReadFromSPBCore(array2);
                if (array3 != null)
                {
                    serialPort.Write(array3, 0, array3.Length);
                    if (base.LogNet != null)
                    {
                        base.LogNet.WriteDebug(ToString(), "[" + serialPort.PortName + "] Ascii " + StringResources.Language.Send + "：" + Encoding.ASCII.GetString(array3.RemoveLast(2)));
                    }
                    if (base.IsStarted)
                    {
                        RaiseDataReceived(sender, array2);
                    }
                }
            }
        }

        protected override void Dispose(bool disposing)
        {
            if (disposing)
            {
                xBuffer.Dispose();
                yBuffer.Dispose();
                mBuffer.Dispose();
                dBuffer.Dispose();
                rBuffer.Dispose();
                wBuffer.Dispose();
                if (serialPort != null)
                {
                    serialPort.Dispose();
                }
            }
            base.Dispose(disposing);
        }

        public override string ToString()
        {
            return String.Format("FujiSPBServer[{0}]", base.Port);
        }
    }
}